package jmu.tools.file.sync.util.action ;


import java.io.File ;
import java.io.FileInputStream ;
import java.io.FileNotFoundException ;
import java.io.FileOutputStream ;
import java.io.IOException ;
import java.text.DecimalFormat ;

import jmu.tools.file.sync.util.AbstractAction ;
import jmu.tools.file.sync.util.LogManager ;
import jmu.tools.file.sync.util.SyncResult ;
import jmu.tools.file.sync.util.ThreadManager ;
import jmu.tools.file.sync.util.LogManager.Type ;


/**
 * @author vkushnir
 */
public class DownloadAction extends AbstractAction
{
    private static final double KB_PER_SECOND = (double) 1000 / 1024 ;
    private static final DecimalFormat formatter = new DecimalFormat ( "#################.######" ) ;

    private static String getDownloadSpeed ( final File source, final long time )
    {
        if ( source.isDirectory () || 0 == source.length () || 0 == time )
        {
            return "0" ;
        }

        return formatter.format ( KB_PER_SECOND * source.length () / time ) ;
    }

    private boolean force = false ;

    /**
     * Create new synchronize action
     * 
     * @param log
     * @param result
     * @param force
     */
    public DownloadAction ( final LogManager log, final SyncResult result, final boolean force )
    {
        super ( log, result ) ;
        this.force = force ;
    }

    @Override
    public void handle ( final ThreadManager input, final File source, final File destination )
    {
        // track start time
        final long start = System.currentTimeMillis () ;

        // process
        final String result = process ( source, destination ) ;

        // track end time
        final long time = getTime ( start ) ;

        // prepare output
        final StringBuilder b = new StringBuilder () ;
        b.append ( source.getAbsolutePath () ) ;
        if ( destination.isFile () && result.equals ( STATUS_OK ) )
        {
            b.append ( SEPARATOR_BIG ).append ( getDownloadSpeed ( source, time ) ).append ( " KB/s" ) ;
        }
        else if ( result.equals ( STATUS_FAIL ) )
        {
            b.append ( SEPARATOR ).append ( destination.getAbsolutePath () ) ;
        }
        b.append ( NEWLINE ) ;

        Type type = null ;
        if ( source.isDirectory () )
        {
            System.out.println ( "not expected behavior" ) ;
            type = result.equals ( STATUS_FAIL ) ? Type.DIR_FAIL : Type.DIR ;
        }
        else if ( source.isFile () )
        {
            type = result.equals ( STATUS_FAIL ) ? Type.FILE_FAIL : Type.FILE ;
        }
        else
        {
            // impossible case
            // this should not happen
        }

        // in impossible case this will be ignored
        getLog ().log ( type, b.toString () ) ;
    }

    private String process ( final File source, final File destination )
    {
        // handle directory
        if ( source.isDirectory () )
        {
            destination.mkdirs () ;

            return STATUS_OK ;
        }

        //
        // handle file
        //

        // update counter
        getResult ().synchronize ( source ) ;

        // create parent folder
        final File folder = destination.getParentFile () ;
        if ( !folder.exists () )
        {
            folder.mkdirs () ;
        }

        // copy file only when forced or destination file is missing or not the same
        if ( !force && destination.exists () && (source.length () == destination.length ()) )
        {
            return STATUS_SKIP ;
        }

        // delete target file when forced
        if ( force && destination.exists () )
        {
            destination.delete () ;
        }

        try
        {
            final FileInputStream fis = new FileInputStream ( source ) ;
            final FileOutputStream fos = new FileOutputStream ( destination ) ;

            final byte[] buf = new byte[1024] ;
            int i = 0 ;
            while ( (i = fis.read ( buf )) != -1 )
            {
                fos.write ( buf, 0, i ) ;
            }
            fis.close () ;
            fos.close () ;

            getResult ().download ( source ) ;
            return STATUS_OK ;
        }
        catch ( final FileNotFoundException e )
        {
            error ( "file download failed: " + e.getLocalizedMessage () ) ;
            getResult ().fail ( source ) ;
            return STATUS_FAIL ;
        }
        catch ( final IOException e )
        {
            error ( "file download failed: " + e.getLocalizedMessage () ) ;
            getResult ().fail ( source ) ;
            return STATUS_FAIL ;
        }
    }
}
